package com.jacky.flutter_screen_recording;

import static com.jacky.flutter_screen_recording.AppUtils.type;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.jacky.log.Logger;
import com.jacky.path_provider_ex.*;
import com.meituan.flutter_logan.FlutterLoganPlugin;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.NetworkInterface;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;


/**
 * Created by lixinquan on 2018/12/20.
 */
public class CheckDialog extends Dialog {

    public interface OnCheckListener {
        void onCheckResult(CheckDialog dialog, boolean success);
    }

    CheckDialog(Context context, String dirName, boolean ignoreSim) {
        super(context, R.style.DialogTransparent);
        IGNORE_ALL = ignoreSim;
        this.dirName = dirName;
    }

    private final boolean IGNORE_ALL;
    private final String dirName;

    ListView mRecyclerView;
    View mChecking;
    View mCheckResult;
    View mCheckToast;
    ImageView mCheckLogo;
    TextView mCheckTitle;
    TextView mCheckSubTitle;
    TextView mCheckReason;
    TextView mCheckOk;
    ProgressBar mProgressBar;

    View mDialogView;
    TextView mTipTitle;
    TextView mTipContent;
    TextView mTipBtnCancel;
    TextView mTipBtnOk;
    TextView mTipBtnSingle;

    private int step = 0;
    private long time;
    private ItemAdapter mAdapter;
    private FileOutputStream mOutputStream;
    private OnCheckListener mListener;
    private TelephonyManagerProxy mTelephonyManagerProxy;
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    CheckDialog setOnCheckListener(long startTime, OnCheckListener listener) {
        mListener = listener;
        time = startTime;
        return this;
    }

    private void findView() {
        mRecyclerView = findViewById(R.id.recycler_view);
        mRecyclerView.setDividerHeight(0);
        mChecking = findViewById(R.id.checking);
        mCheckResult = findViewById(R.id.check_result);
        mCheckToast = findViewById(R.id.toast);
        mCheckLogo = findViewById(R.id.check_logo);
        mCheckTitle = findViewById(R.id.check_title);
        mCheckSubTitle = findViewById(R.id.check_subtitle);
        mCheckReason = findViewById(R.id.check_reason);
        mCheckOk = findViewById(R.id.tvOk);
        mProgressBar = findViewById(R.id.progressBar);

        mDialogView = findViewById(R.id.dialog_tip);
        mTipTitle = findViewById(R.id.title);
        mTipContent = findViewById(R.id.tv_content);
        mTipBtnCancel = findViewById(R.id.btnCancel);
        mTipBtnOk = findViewById(R.id.btnOk);
        mTipBtnSingle = findViewById(R.id.btn_single);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.check_dialog);
        findView();
        setCancelable(false);

        mRecyclerView.setAdapter(mAdapter = new ItemAdapter());
        mTelephonyManagerProxy = new TelephonyManagerProxy(getContext());

        new InnerAsyncTask().execute();
    }

    /**
     * 上传 清洁性文件相关信息
     */
    private void uploadInspection(long currentTime,String reason) {
//        File file = new File(dirName, "/report.txt");
//        boolean checkPass = reason == null && file.exists();
        setCancelable(true);
        setCanceledOnTouchOutside(true);
        boolean success = reason == null;
        if(mListener != null) {
            mListener.onCheckResult(CheckDialog.this, success);
        }
        mCheckToast.setVisibility(success ? View.VISIBLE : View.GONE);
    }

    /**
     * 显示提示弹窗
     */
    private void showDialogTips(String content, View.OnClickListener okListener, View.OnClickListener cancelListener){
        mTipTitle.setText("检查信息保存失败");
        mTipContent.setText(content);
        mTipBtnOk.setText("重试");
        mTipBtnOk.setOnClickListener(v->{
            mDialogView.setVisibility(View.GONE);
            if(okListener != null)okListener.onClick(v);
        });
        mTipBtnCancel.setText("取消取证");
        mTipBtnCancel.setOnClickListener((v)->{
            mDialogView.setVisibility(View.GONE);
            if(cancelListener != null)cancelListener.onClick(v);
        });
        ((View)mTipBtnSingle.getParent()).setVisibility(View.GONE);
        mDialogView.setVisibility(View.VISIBLE);
    }

    @SuppressLint({"MissingPermission", "HardwareIds"})
    private boolean checkPhoneInfo() {
        writeToReport("------------------------手机信息-------------------------");
        writeToReport("手机型号：" + Build.MODEL);
        writeToReport("序列号：" + TelephonyManagerProxy.getSerial());
        writeToReport("IMEI：" + TelephonyManagerProxy.getImei());
        writeToReport("Android版本：" + Build.VERSION.RELEASE);
        writeToReport("内核版本：" + getLinuxCore_Ver());
        writeToReport("基带版本：" + getBaseband_Ver());
        SecureCheck check = new SecureCheck();
        boolean root = check.checkIsRoot();
        writeToReport("Root情况：" + (root ? "已ROOT" : "没有ROOT"));
        writeToReport("开发者模式：" + (check.checkDevelopEnabled(getContext()) ? "已开启" : "关闭"));
        writeToReport("ADB调试：" + (check.checkAdbEnabled(getContext()) ? "已开启" : "关闭"));
        return !root;
    }
    private boolean checkWifiInfo() {
        writeToReport("\r\n----------------------无线网络信息-----------------------");
        WifiManager manager = (WifiManager) getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        boolean b = !manager.isWifiEnabled() || manager.setWifiEnabled(false);
        writeToReport("\r\nWIFI:" + (b ? "已关闭" : "未关闭"));
        Logger.w("setWifiEnabled:", !b);
        return b;
    }

    /*
     * 判断设备 是否使用代理上网
     */
    private boolean isWifiProxy() {
        int proxyPort;
        String proxyAddress = System.getProperty("http.proxyHost");
        String portStr = System.getProperty("http.proxyPort");
        proxyPort = Integer.parseInt((portStr != null ? portStr : "-1"));
        return (!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);
    }

    @SuppressLint("MissingPermission")
    private boolean checkMobileInfo() {
//        TelephonyManager manager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
        writeToReport("\r\n----------------------移动网络信息-----------------------");
        String simOperator = mTelephonyManagerProxy.getSimOperator();
        if(TextUtils.isEmpty(simOperator)) return false;

        writeToReport("是否插入SIM卡：有");

        String name = "未知";
        switch (simOperator) {
            case "46001" :
            case "46006" :
                name = "中国联通"; break;
            case "46003" :
            case "46005" :
            case "46011" :
                name = "中国电信"; break;
            case "46000" :
            case "46007" :
            case "46002" : name = "中国移动"; break;
        }
        writeToReport("运营商：" + name);
        writeToReport("运营商代码：" + simOperator);

        int networkType = mTelephonyManagerProxy.getNetworkType();
        String nw = "";
        switch (networkType) {
            // 2G网络
            case TelephonyManager.NETWORK_TYPE_GPRS:
            case TelephonyManager.NETWORK_TYPE_CDMA:
            case TelephonyManager.NETWORK_TYPE_EDGE:
            case TelephonyManager.NETWORK_TYPE_1xRTT:
            case TelephonyManager.NETWORK_TYPE_IDEN:
                nw = "2G"; break;
            // 3G网络
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
            case TelephonyManager.NETWORK_TYPE_UMTS:
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
            case TelephonyManager.NETWORK_TYPE_HSDPA:
            case TelephonyManager.NETWORK_TYPE_HSUPA:
            case TelephonyManager.NETWORK_TYPE_HSPA:
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
            case TelephonyManager.NETWORK_TYPE_EHRPD:
            case TelephonyManager.NETWORK_TYPE_HSPAP:
                nw = "3G"; break;
            // 4G网络
            case TelephonyManager.NETWORK_TYPE_LTE:
                nw = "4G"; break;
            case 20 :
                nw = "5G"; break;
            default:
                nw = "未知"; break;
        }
        writeToReport("网络制式：" + nw);
        return true;
    }

    /**
     * 是否正在使用VPN
     */
    public static boolean isVpnUsed() {
        try {
            Enumeration<NetworkInterface> niList = NetworkInterface.getNetworkInterfaces();
            if(niList != null) {
                for (;niList.hasMoreElements();) {
                    NetworkInterface intf = niList.nextElement();
                    if(!intf.isUp() || intf.getInterfaceAddresses().size() == 0) {
                        continue;
                    }
                    Log.d("-----", "isVpnUsed() NetworkInterface Name: " + intf.getName());
                    if ("tun0".equals(intf.getName()) || "ppp0".equals(intf.getName())){
                        return true; // The VPN is up
                    }
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return false;
    }

    private void checkAppList() {
        writeToReport("\r\n----------------------已安装软件列表-------------------------");
        PackageManager manager = getContext().getPackageManager();
        List<PackageInfo> list = manager.getInstalledPackages(0);
        for(PackageInfo info : list) {
            writeToReport("软件名称:" + info.applicationInfo.loadLabel(manager));
            writeToReport("软件版本:" + info.versionName);
            writeToReport("包名:" + info.packageName);
            writeToReport("应用签名:" + getSHA1Signature(manager, info.packageName));
            writeToReport("安装路径:" + info.applicationInfo.sourceDir);
            writeToReport("安装时间:" + AppUtils.formatTime("yyyy-MM-dd HH:mm:ss", info.firstInstallTime));
            writeToReport("更新时间:" + AppUtils.formatTime("yyyy-MM-dd HH:mm:ss", info.lastUpdateTime));
            String n = manager.getInstallerPackageName(info.packageName);
            writeToReport("安装来源:" + (n == null ? "系统应用" : n));
            writeToReport("");
        }
    }

    private String getSHA1Signature(PackageManager manager, String packageName) {
        try {
            PackageInfo info = manager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
            byte[] cert = info.signatures[0].toByteArray();
            MessageDigest md = MessageDigest.getInstance("SHA1");
            byte[] publicKey = md.digest(cert);
            StringBuilder hexString = new StringBuilder();
            for (int i = 0; i < publicKey.length; i++) {
                String appendString = Integer.toHexString(0xFF & publicKey[i]).toUpperCase(Locale.US);
                hexString.append(":");
                if (appendString.length() == 1) {
                    hexString.append("0");
                }
                hexString.append(appendString);
            }
            return hexString.substring(1);
        } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private class ItemAdapter extends BaseAdapter {

        LayoutInflater mInflater = getLayoutInflater();
        List<CheckStatusBean> list = new ArrayList<>();

        ItemAdapter() {
            list.add(new CheckStatusBean("检测手机时间","手机时间与北京时间偏差较大，请开启自动同步网络时间的功能"));
            list.add(new CheckStatusBean("检测手机信息","手机已root，请更换手机进行取证"));
            list.add(new CheckStatusBean("检测手机网络","未关闭wifi，请使用手机流量进行取证"));
            list.add(new CheckStatusBean("检测软件列表",null));
        }

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public CheckStatusBean getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView == null) {
                convertView = mInflater.inflate(R.layout.check_dialog_item, null, false);
            }
            TextView statusView = convertView.findViewById(R.id.status);
            TextView nameView = convertView.findViewById(R.id.name);
            ImageView logoView = convertView.findViewById(R.id.logo);
            View line = convertView.findViewById(R.id.line);
            TextView posView = convertView.findViewById(R.id.pos);

            CheckStatusBean item = getItem(position);
            nameView.setText(item.content);
            line.setVisibility(position == getCount() - 1 ? View.GONE : View.VISIBLE);
            if(step < position) {
                posView.setText(String.valueOf(position + 1));
                statusView.setText("待进行");
                logoView.setImageResource(R.mipmap.check_wait);
            } else {
                posView.setText("");
                if(step == position) {
                    statusView.setText("进行中");
                    logoView.setImageResource(R.mipmap.check_running);
                } else if(!item.isError()){
                    statusView.setText("已完成");
                    logoView.setImageResource(R.mipmap.check_finish);
                }else {
                    statusView.setText("未通过");
                    logoView.setImageResource(R.mipmap.check_error);
                    nameView.setText("原因："+item.error);
                }
            }
            return convertView;
        }
    }

    /**
     * CORE-VER
     * 内核版本
     * return String
     */
    private String getLinuxCore_Ver() {
        String result = SecureCheck.exec("cat /proc/version");
        if(TextUtils.isEmpty(result)) return "";
        if(result.endsWith("denied")) return "Permission denied";

        int i1 = result.indexOf("version") + 8;
        int i2 = result.indexOf(' ', i1 + 1);
        return result.substring(i1, i2);
    }

    /**
     * BASEBAND-VER
     * 基带版本
     * return String
     */
    @SuppressLint("PrivateApi")
    private String getBaseband_Ver(){
        String Version = "";
        try {
            Class cl = Class.forName("android.os.SystemProperties");
            Method m = cl.getMethod("get", new Class[] { String.class,String.class });
            Object result = m.invoke(null, new Object[]{"gsm.version.baseband", "no message"});
            Version = (String)result;
        } catch (Exception e) {
            Logger.e(e);
        }
        return Version;
    }

    private static boolean isExecutable(String filePath) {
        String result = SecureCheck.exec("ls -l " + filePath);
        if(TextUtils.isEmpty(result) || result.length() < 4) {
            return false;
        }
        char flag = result.charAt(3);
        if (flag == 's' || flag == 'x') {
            return true;
        }
        return false;
    }

    private void writeToReport(String info) {
//        Logger.i(info);
        if(mOutputStream == null) return;
        try {
            mOutputStream.write(info.getBytes(StandardCharsets.UTF_8));
            mOutputStream.write('\r');
            mOutputStream.write('\n');
        } catch (IOException e) {
            Logger.e(e);
        }
    }

    @SuppressLint("StaticFieldLeak")
    private class InnerAsyncTask extends AsyncTask<Integer, Integer, Integer> {

        private final long currentTime = System.currentTimeMillis();

        private void showMsg(int index,CharSequence sequence) {
            if(index >= 0 && index < mAdapter.getCount()) {
                mAdapter.getItem(index).setError(sequence.toString());
            }
            mHandler.post(() -> AppUtils.showToast(getContext(), sequence));
        }

        @Override
        protected Integer doInBackground(Integer... integers) {
            File file = null;
            boolean ignoreAll = IGNORE_ALL;
            boolean isOk = true;
            try {
                if(time > 0 && !TextUtils.isEmpty(dirName)) {
                    file = new File(dirName, "/report.txt");
                    mOutputStream = new FileOutputStream(file);
                }
                publishProgress(0);

                if(!(Math.abs(currentTime - time) <= 600_000)) {
                    showMsg(0,"手机时间与北京时间\n偏差大于10分钟");
                    isOk = false;
                }

                publishProgress(1);

                if(!checkPhoneInfo()) {
                    showMsg(1, "手机经过root");
                    isOk = false;
                }

                publishProgress(2);

                if(!checkMobileInfo()) {
                    showMsg(2, "SIM卡检测失败");
                    isOk = isOk && ignoreAll;
                }

                publishProgress(2);
                if(!checkWifiInfo()) {
                    showMsg(2, "WiFi未关闭");
                    isOk = isOk && ignoreAll;
                }

                if(isVpnUsed()) {
                    showMsg(2, "您开启了VPN连接");
                    isOk = false;
                }

                publishProgress(3);

                long mem = totalMemory();
                if(mem < 400_0000) { //4G内存
                    showMsg(-1, "手机配置太差，建议更换取证手机");
                } else if(mem < 600_0000) { //6G内存
                    showMsg(-1, "手机配置较低，请留意取证情况");
                }

                checkAppList();
                publishProgress(4);
            } catch (Exception ex) {
                Logger.e(ex);
                isOk = false;
            }
            AppUtils.close(mOutputStream);

            if(!isOk){
                step = -1;
                publishProgress(-1);
                if(file != null) file.delete();
            }
            return 5;
        }

        protected void publishProgress(int v) {
            super.publishProgress(v);
            if(v > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignored) {
                }
            }
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            step = values[0];
            mAdapter.notifyDataSetChanged();
        }

        @Override
        protected void onPostExecute(Integer integer) {
            mChecking.setVisibility(View.GONE);
            mCheckResult.setOnClickListener(v->dismiss());
            mCheckToast.setOnClickListener(v->dismiss());

            if (step > 0) {
                uploadInspection(currentTime,null);
            } else {
                StringBuilder sb = new StringBuilder("失败原因：\n");
                int i = 1;
                for(int j = 0, size = mAdapter.getCount();j < size;j++) {
                    CheckStatusBean bean = mAdapter.getItem(j);
                    if(bean.code == CheckStatusBean.CODE_ERROR){
                        sb.append(i++).append("、").append(bean.reason).append("；");
                    }
                }
                mCheckResult.setVisibility(View.VISIBLE);
                mCheckTitle.setText("检查失败");
                mCheckReason.setText(sb);
                uploadInspection(currentTime,sb.toString());
            }
        }
    }


    @Override
    public void show() {
        if(!(getContext() instanceof Activity)) {
            getWindow().setType(type());
        }
        super.show();
    }

    //查看手机内存
    private long totalMemory() {
        try {
            String result = SecureCheck.exec("cat /proc/meminfo");
            result = result.split("\n")[0]; //单位是 KB
            String[] results = result.split(" ");
            result = results[results.length - 2];
            return Long.parseLong(result);
        } catch (Exception e) {
            Logger.e(e);
            FlutterLoganPlugin.w("get totalMemory fail." + e.getMessage(), 1);
            return 8000001; //8G内存
        }
    }
}
